package gebzebelediye_server;

import java.awt.image.*;
import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.imageio.ImageIO;
import com.asprise.util.pdf.PDFImageWriter;
import java.util.logging.Level;
import java.util.logging.Logger;


class ClientHandler implements Runnable {

    
    Socket socket = new Socket();
    static boolean cikis;
    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    static ArrayList<Socket> sockets = new ArrayList<Socket>();
    String mevcut_ip;
    private String gonderilecekMesaj = "";
    BufferedReader buffReader;
    InputStream inStream;
    BufferedImage resim;
    String gelenMesaj = "";
    OutputStream outStream;
    static int boyut = 0;
    boolean dosya_alindimi = false;

    ClientHandler(Socket s) {
        socket = s;
        sockets.add(socket);

    }

    @Override
    public void run() {
        Lock lock = new ReentrantLock();
        boolean baglanti = true;
        
        System.out.println("Yeni client baglandi: ");
        cikis = false;
        try {
            outStream = socket.getOutputStream();
            buffReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (IOException ex) {
            System.out.println("Soket IOStream hatasi: " + ex);
            run();
        }

        while (baglanti) {
            lock.lock();
            ip_gir();
            lock.unlock();
            if ("p".equals(gonderilecekMesaj)) {
                dosya_al();
            } else {
                mesaj_al();
                if (cikis) {
                    ip_gir();
                    break;
                }
            }
        }
    }

    /**
     * Clientları listeleyerek serverın hedef ip'yi secmesini saglar
     */
    private void ip_gir() {
        boolean eslesme = false;
        if (sockets.isEmpty()) {
            System.out.println("Bagli Client Bulunmamaktadir...");
        } else {
            System.out.println(" Mevcut clientlar: ");
            for (Socket soket : sockets) {
                System.out.println(soket.getLocalAddress().toString());
            }
            System.out.println("IP giriniz: ");
            try {
                mevcut_ip = input.readLine();
            } catch (IOException ex) {
                System.out.println("ip okunurken hata olustu: " + ex);
                ip_gir();
                return;
            }
            for (Socket soket : sockets) {
                if (soket.getLocalAddress().toString().equals(mevcut_ip)) {
                    eslesme = true;
                    mesaj_oku_gonder();
                }

            }

            if (!eslesme) {
                System.out.println("Yanlis giris yaptiniz!");
                ip_gir();
            }
            return;
        }
    }

    /**
     * Girilen mesajı okur ve bunu tüm clientlara gönderir.
     */
    private void mesaj_oku_gonder() {

        System.out.println("Mesajinizi giriniz, tarayici icin 'p'ye basiniz: ");
        try {
            gonderilecekMesaj = input.readLine();
        } catch (IOException ex) {
            System.out.println("mesaj okunurken hata olustu: " + ex);
            mesaj_oku_gonder();
            return;
        }
        for (Socket soket : sockets) {
            PrintStream ps = null;
            try {
                ps = new PrintStream(soket.getOutputStream());
                ps.println(mevcut_ip + "-" + gonderilecekMesaj);
                if (mevcut_ip.equals(soket.getLocalAddress().toString())) {
                    socket = soket;
                }
            } catch (IOException ex) {
                System.out.println("Sokete veri yazilirken hata olustu.: " + ex);
                mesaj_oku_gonder();
                return;
            }
        }
        return;
    }

    /**
     * Clienttan gelen byte dizisini, parca sırasına göre bir byte arraya atar
     * Bu byte arrayi önce jpg formatına cevirir Daha sonra olusan resmi pdf
     * formatında kaydeder.
     */
    private void dosya_al() {


        dosya_alindimi = true;
        int tekrar = 0;
        int artan = 0;
        try {
            boyut = Integer.parseInt(buffReader.readLine());
        } catch (IOException ex) {
            System.out.println("Karsidan gelen veri bozuk: " + ex);
            dosya_al();
            return;
        }
        System.out.println("boyut:" + boyut);

        tekrar = boyut / Parametre.MAX_DATA_PARCA_BOYUTU;
        artan = boyut % Parametre.MAX_DATA_PARCA_BOYUTU;
        System.out.println("artan:" + artan);
        byte[] datas = new byte[boyut];

        InputStream is = null;
        try {
            is = socket.getInputStream();
        } catch (IOException ex) {
            System.out.println("socket inputstream hatasi: " + ex);
            dosya_al();
            return;
        }

        for (int i = 0; i < tekrar; i++) {
            byte[] okunan = new byte[Parametre.MAX_PARCA_BOYUTU];
            try {
                is.read(okunan);
            } catch (IOException ex) {
                System.out.println("byte dizisine yazma hatasi: " + ex);
                dosya_al();
                return;
            }
            int parcaSirasi = Convert32BitsToInt(okunan);
            byte[] okunanData = getDataFromPacket(okunan);
            int index = parcaSirasi * Parametre.MAX_DATA_PARCA_BOYUTU;
            for (int j = 0; j < Parametre.MAX_DATA_PARCA_BOYUTU; ++j) {
                datas[index++] = okunanData[j];
            }
        }

        if (artan != 0) {
            byte[] okunan = new byte[artan + Parametre.PARCA_HEADER_SIZE];
            try {
                is.read(okunan);
            } catch (IOException ex) {
                System.out.println("byte dizisine yazma hatasi: " + ex);
                dosya_al();
                return;
            }
            int parcaSirasi = Convert32BitsToInt(okunan);
            byte[] okunanData = getDataFromPacket(okunan);
            int index = parcaSirasi * Parametre.MAX_DATA_PARCA_BOYUTU;
            for (int j = 0; j < artan; ++j) {
                datas[index++] = okunanData[j];
            }
        }
        inStream = new ByteArrayInputStream(datas);
        try {
            resim = ImageIO.read(inStream);
        } catch (IOException ex) {
            System.out.println("Pdf icin gerekli resim olusturulamadi.: " + ex);
            dosya_al();
            return;
        }

        PDFImageWriter writer = null;
        try {
            writer = new PDFImageWriter(new FileOutputStream(Parametre.SAVE_PDF_PATH));
        } catch (FileNotFoundException ex) {
            System.out.println("PDF için yol bulunamadi.. :" + ex);

        }

        writer.open();
        writer.addImage(resim);
        writer.close();

        System.out.println("Okuma tamamlandi");
    }

    /**
     * Client tarafından gonderien veriyi okur Client q demişse ona ait soket
     * kapatarak serverdan ayırır.
     */
    private void mesaj_al() {

        System.out.println("Clientin bilgi girmesi  bekleniyor... ");
        try {
            if (dosya_alindimi) {
                buffReader.skip(boyut);
            }

            gelenMesaj = buffReader.readLine();
        } catch (IOException ex) {
            System.out.println("Clienttan bilgi okunurken hata olustu: " + ex);
            mesaj_al();
            return;
        }
        if (!"q".equals(gelenMesaj)) {

            System.out.println(socket.getInetAddress().toString() + " den gelen mesaj: " + gelenMesaj);
        } else if ("q".equals(gelenMesaj)) {
            System.out.println(socket.getInetAddress().toString() + " serverdan ayrildi ");


            sockets.remove(socket);
            for (Socket a : sockets) {
                System.out.println(a);
            }
            try {
                socket.close();
            } catch (IOException ex) {
                System.out.println("Soket kapatilmadi: " + ex);
               
            }
            cikis = true;
}
    }

    /**
     * verilen data dizisindeki header bilgisinden paketin kaçıncı sırada
     * olduğunu hesaplar.
     *
     * @param sayi
     * @return
     */
    private static int Convert32BitsToInt(byte[] sayi) {
        int retValue = 0;
        return retValue;
    }

    /**
     * verilen data dizisi içerisindeki header bilgisini çıkartarak saf datayı
     * return eder.
     *
     * @param datas
     * @return
     */
    private static byte[] getDataFromPacket(byte[] datas) {
        byte[] retValue = new byte[Parametre.MAX_DATA_PARCA_BOYUTU];
        int baslangic_degeri=0;
        for (int i = Parametre.PARCA_HEADER_SIZE; i < Parametre.MAX_PARCA_BOYUTU; i++) {
            retValue[baslangic_degeri++] = datas[i];
        }
        return retValue;
    }
}
